﻿using System;
using System.Diagnostics;
using System.Security;
using ITSP.SharePoint.CacheRefresh.Common;
using ITSP.SharePoint.CacheRefresh.Utilities;
using Microsoft.Practices.SharePoint.Common.Configuration;
using Microsoft.Practices.SharePoint.Common.ServiceLocation;
using Microsoft.SharePoint.Administration;

namespace ITSP.SharePoint.CacheRefresh
{
    /// <summary>
    /// A Configuration Manager that uses the SharePoint Farm properties to store the configuration.
    /// </summary>
    /// <createdby>Simon Doy</createdby>
    public abstract class BaseSPConfigurationManager : BaseConfigurationManager
    {
        #region Constructors

        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="cacheListHostUrl">The Url of the Web Application / Site that is hosting the Cache List. This is used by the Configuration Manager.</param>
        protected BaseSPConfigurationManager(string cacheListHostUrl)
            :base(cacheListHostUrl)
        {
            
        }

        #endregion
        
        /// <summary>
        /// Saves the Configuration object to the SPFarm Property Bag using <see cref="BaseSPConfigurationManager.ConfigurationStorageKey"/>.
        /// This operation will also refresh the cache using the Cache Dependencies.
        /// </summary>
        /// <param name="configuration">The Configuration object that is to be saved to the SharePoint Property Bag.</param>
        /// <returns>True/False depending on the success of the Save operation</returns>
        public override bool Save<T>(T configuration)
        {
            bool bSuccess = false;
            logger.TraceToDeveloper("BaseSPConfigurationManager::Save() Enter", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);

            try
            {
                logger.TraceToDeveloper(String.Format("Saving Configuration {0}", configuration.GetType()), 0, TraceSeverity.Monitorable, Constants.Logging.DefaultLoggingCategory);

                //check permissions
                IConfigManager configManager = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
                if(!configManager.CanAccessFarmConfig)
                {
                    throw new SecurityException("User does not have access to save Farm Property Bag.");
                }

                string serializedConfigurationString = DataSerialisationUtils.Serialize<T>(configuration);

                IPropertyBag farmPropertyBag = configManager.GetPropertyBag(ConfigLevel.CurrentSPFarm);
                configManager.RemoveKeyFromPropertyBag(ConfigurationStorageKey, farmPropertyBag);
                configManager.SetInPropertyBag(ConfigurationStorageKey, serializedConfigurationString, farmPropertyBag);

                AddToCache<T>(configuration);
                bSuccess = true;

                Refresh();

                logger.TraceToDeveloper("Saved Configuration Successfully", 0, TraceSeverity.Monitorable, Constants.Logging.DefaultLoggingCategory);

            }
            catch (Exception ex)
            {
                logger.TraceToDeveloper(ex, 10000, TraceSeverity.High, Constants.Logging.DefaultLoggingCategory);
                Trace.TraceError("BaseSPConfigurationManager::Save() {0}", ex.Message);
                throw ex;
            }

            logger.TraceToDeveloper("BaseSPConfigurationManager::Save() Exit", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);

            return bSuccess;
        }

        /// <summary>
        /// Loads in the configuration from SharePoint Farm Property Bag.
        /// </summary>
        /// <returns>Returns the Generic object from SharePoint Property Bag.</returns>
        /// <exception cref="ConfigurationException">Returned if no configuration found.</exception>
        public override T Load<T>()
        {
            logger.TraceToDeveloper("BaseSPConfigurationManager::Load() Enter", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            T configuration = default(T);

            try
            {
                configuration = base.GetFromCache<T>();
                if (configuration == null)
                {
                    IConfigManager configManager = SharePointServiceLocator.GetCurrent().GetInstance<IConfigManager>();
                    IPropertyBag farmPropertyBag = configManager.GetPropertyBag(ConfigLevel.CurrentSPFarm);
                    if (configManager.ContainsKeyInPropertyBag(ConfigurationStorageKey, farmPropertyBag))
                    {
                        try
                        {
                            string deserializedString = configManager.GetFromPropertyBag<string>(ConfigurationStorageKey, farmPropertyBag);
                            configuration = DataSerialisationUtils.Deserialize<T>(deserializedString);

                            //Add the item to the cache!
                            AddToCache<T>(configuration);
                        }
                        catch (Exception ex)
                        {
                            logger.TraceToDeveloper(ex, "BaseConfigurationManager::Load() Exception", 10000, TraceSeverity.Medium, Constants.Logging.DefaultLoggingCategory);
                        }

                    }
                }
            }
            catch (ConfigurationException ex)
            {
                logger.TraceToDeveloper(ex, 10000, TraceSeverity.Medium, Constants.Logging.DefaultLoggingCategory);
                throw ex;
            }

            logger.TraceToDeveloper("BaseSPConfigurationManager::Load() Exit", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);

            return configuration;
        }
       
    }

}
